const fs = require("fs");
const util = require("util");
const path = require("path");
const EOL = require("os").EOL;

// 2个磁盘io方法promise化
const [readDir, readFile] = ["readdir", "readFile"].map(functionName =>
    util.promisify(fs[functionName])
);

/**
 * @description 每一节(##)具体文档
 * @param {Object} option
 * @param {String} option.name - 接口名/描述
 * @param {String} option.url - 接口地址
 * @param {String} option.type - http method
 * @param {String} option.contentType - 接口接受的request的content-type
 * @param {String} option.reqHeaders - 请求头
 * @param {String} option.reqBody - 请求主体
 * @param {String} option.reqExample - 请求主体例子
 * @param {String} option.resBody - 响应主体
 * @param {String} option.resExample - 响应主体的例子
 */
class DocChunk {
    constructor(option) {
        for (const key in option) {
            if (option.hasOwnProperty(key)) {
                this[key] = option[key];
            }
        }
        // table造型的数据表头和表身分离
        this.reqHeadersKeys = this.pickHeadArrayFromMarkdown(option.reqHeaders);
        this.reqHeadersData = this.pickRowArrayFromMarkdown(option.reqHeaders, this.reqHeadersKeys);
        this.reqBodyKeys = this.pickHeadArrayFromMarkdown(option.reqBody);
        this.reqBodyData = this.pickRowArrayFromMarkdown(option.reqBody, this.reqBodyKeys);
        this.resBodyKeys = this.pickHeadArrayFromMarkdown(option.resBody);
        this.resBodyData = this.pickRowArrayFromMarkdown(option.resBody, this.resBodyKeys);
        // 移除不再有用的属性
        this.reqHeaders = undefined;
        this.reqBody = undefined;
        this.resBody = undefined;
    }
    /**
     * @description 从table类型里提取头部
     * @param {String} str - 表格类型的markdown
     * @returns {String[]}
     */
    pickHeadArrayFromMarkdown(str) {
        // 获取第一行
        const firstLine = str.split(EOL).filter(s => s.trim())[0];
        return firstLine
            .split("|")
            .filter(s => s.trim())
            .map(s => s.trim());
    }
    /**
     * @description 从table类型里提取row
     * @param {String} str - 表格类型的markdown
     * @param {String[]} header - 通过pickHeadArrayFromMarkdown方法算出来的表头
     * @returns {Array<Object[]>}
     */
    pickRowArrayFromMarkdown(str, header) {
        const arr = str.split(EOL).filter(s => s.trim());
        // 去除头两行
        const result = arr.slice(2);
        return result.map(s => {
            const rowValues = s
                .split("|")
                .filter(s => s.trim())
                .map(s => s.trim());
            const o = Object.create(null);
            header.forEach((key, index) => {
                o[key] = rowValues[index];
            });
            return o;
        });
    }
}
/**
 * @description 每一个md文档文件
 */
class Doc {
    constructor(filename, filePath) {
        this.filename = filename;
        this.filePath = filePath;
        this.title = "";
        // DocChunk[]
        this.docChunks = [];
    }
    async loadData() {
        let fileStr = await readFile(this.filePath, { encoding: "utf-8" });
        const title = fileStr.match(/^#\s.+$/m)[0];
        this.title = title.replace("#", "").trim();
        // 正文移除标题方便分拣
        return fileStr.replace(title, "");
    }
    /**
     * @description 分拣出节
     * @param {String} fileStr - 文件全部内容
     * @returns {Array<String>}
     */
    split(fileStr) {
        const arr = fileStr
            .split(/[^#]##\s/)
            .filter(s => s.trim())
            .map(s => `## ${s}`);
        return arr;
    }
    /**
     * @description 具体chunk分析
     * @param {String} chunkStr - 节的全部内容
     * @returns {DocChunk}
     */
    analyze(chunkStr) {
        const getString = regWithCaptureGroup => {
            let s = "";
            try {
                s = chunkStr.match(regWithCaptureGroup)[1].trim();
            } catch (error) {
                s = "<本项缺失>";
            }
            return s;
        };

        const name = getString(/^##\s(.+)$/m);
        const url = getString(/\*\*URL:\*\*\s(.+)$/m);
        const type = getString(/\*\*Type:\*\*\s(.+)$/m);
        const contentType = getString(/\*\*Content-Type:\*\*\s(.+)$/m);
        const reqHeaders = getString(
            /\*\*Request-headers:\*\*(.+)\*\*Request-parameters:\*\*/s
        );
        const reqBody = getString(
            /\*\*Request-parameters:\*\*(.+)\*\*请求示例:\*\*/s
        );
        const reqExample = getString(
            /\*\*请求示例:\*\*(.+)\*\*响应数据:\*\*/s
        ).replace(/`/g, "");
        const resBody = getString(
            /\*\*响应数据:\*\*(.+)\*\*返回示例:\*\*/s
        );
        const resExample = getString(/\*\*返回示例:\*\*(.+)/s).replace(
            /`/g,
            ""
        );
        return new DocChunk({
            name,
            url,
            type,
            contentType,
            reqHeaders,
            reqBody,
            reqExample,
            resBody,
            resExample,
        });
    }
}

// Array<Doc>
const dependence = [];

const core = async () => {
    const uri = path.join(__dirname, "md-files");
    // string[]
    const fileList = await readDir(uri);
    for (const filename of fileList) {
        const filePath = path.join(uri, filename);
        const doc = new Doc(filename, filePath);
        const chunks = doc.split(await doc.loadData());
        // DocChunk[]
        doc.docChunks = chunks.map(s => doc.analyze(s));
        dependence.push(doc);
    }
    return dependence;
};

module.exports = core;
